Document Title: [Boris - Understanding Mame drivers 1.html (html file)]
Understanding MAME Drivers Volume 1: Data Structures v1.0 by Daniel Boris Table of Contents 1.0 Introduction 2.0 /drivers/rockola.c 2.1 GameDriver Structure 2.2 MachineDriver Structure 2.3 ROM Definition 2.4 MemoryReadAddress Structure 2.5 MemoryWriteAddress Structure 2.6 Input Ports 2.7 GfxDecodeInfo Structure 2.8 GfxLayout Structure 3.0 Linking in the Driver 1.0 Introduction The purpose of this document is to help people understand how a MAME driver works and to help people who may want to write their own MAME drivers. A driver is a set of files that are part of the MAME source that define the operation of a specific arcade game. For this document I will be describing the driver for the game Zarzon. Before reading this document you may want to read over my Decoding Schematics document which describes the hardware of Zarzon. The driver I am going to describe is based on the MAME 0.33 source code for DOS. Some of the things described here may not apply to other versions of the MAME source. The Zarzon driver can be found in two files, /driver/rockola.c and /vidhrdw/rockola.c. It is found in this file because it's hardware is similar to some of the other Rockola games. The data structures that make up MAME are linked together in layers so you may not understand some of the things in the higher level layers until you read about the lower level layers. 2.0 /drivers/rockola.c The first part of the driver is contained in the /drivers/ directory. The files in this directory are the main part of a MAME driver. They contain all the data structures that define how the machine that is being emulated works. 2.1 GameDriver Structure The highest level structure for each game in MAME is the GameDriver structure. The GameDriver structure for Zarzon looks like this (the numbers at the start of each line are there so I can refer to the lines easily in the text): 1 struct GameDriver zarzon_driver = 2 { 3 __FILE__, 4 &satansat_driver, 5 "zarzon", 6 "Zarzon", 7 "1981", 8 "[SNK] (Taito America license)", 9 "Dan Boris\nTheo Philips", 10 0, 11 &satansat_machine_driver, 12 13 zarzon_rom, 14 0, 0, 15 0, 16 0, /* sound_prom */ 17 18 satansat_input_ports, 19 20 satansat_color_prom,0,0, 21 ORIENTATION_ROTATE_90, 22 23 0, 0 24 }; Line 1 starts the declaration of the driver. The name of the structure, in this case zarzon_driver, is usually the name that is used to start the game followed by "_driver". Line 3 is the source file that this driver is contained in. It should always be set to: __FILE__ If this game is a clone of another game, then line 4 should be a pointer to the GameDriver structure of the parent game. For example Zarzon is a clone of Satan of Saturn. Line 5 is the name that is used to start the game and also the name of the directory/zipfile where the ROMS for the game can be found. It has to be 8 characters or less and must be unique to each game. Line 6 is the full name of the game. Line 7 is the year the game was released and line 8 is the name of the company that released it. Line 9 is the credits that are displayed when the driver is started. Line 10 is designed to hold various flag values. Currently the only flag that is defined is GAME_NOT_WORKING which indicates that a game currently does not work. Line 11 is a pointer to the MachineDriver structure that defines how the hardware for this machine works. It is possible for more the one game to run on the same machine hardware. For example there are a bunch of different versions of Pac-Man that all run on the same hardware, so each of the games will have it's own GameDriver structure, but they can all share the same MachineDriver structure. Line 13 is a pointer to a structure that defines what ROMS the game uses and where to load them. The two zeros in line 14 can be a pointer to a routine to decrypt roms and a pointer to a routine to decrypt opdcodes in the ROM. These are optional and are not used by this game. Line 15 is a pointer to an array of samples names that the game is going to use to produce sound. This game does not currently use samples so this is left as 0. Line 16 is a pointer to sound ROM data. This is not used by this game. Line 18 is a pointer to the InputPort structure for this game. Note that this is defined as part of the game not as part of the hardware since it is responsible for things like dipswitches that change function from game to game. The three values in line 20 are pointers to palette and color tables that are stored in the driver. These tables are currently being removed from the code and put into the ROM images like they where in the original games, so it is best to load these tables from disk. The first value is a pointer to the palette prom for the game, the second is a pointer to a table of actual color values that make up the game's palette and the third is a pointer to a color lookup table. Line 21 defines the orientation of the games monitor. Look at driver.h for the legal values for this line. Line 23 are pointers to routines that are used to load and save high scores. Zarzon currently doesn't have high score saving so these are 0. 2.2 MachineDriver Structure The next level data structure is MachineDriver. This defines the hardware that this game runs on. In this situation Zarzon is a clone of Satan of Saturn so the MachineDriver structure is called satansat_machine_driver. 1 static struct MachineDriver satansat_machine_driver = 2 { 3 /* basic machine hardware */ 4 { 5 { 6 CPU_M6502, 7 11289000/16, /* 700 kHz */ 8 0, 9 satansat_readmem,satansat_writemem,0,0, 10 satansat_interrupt,2 11 }, 12 }, 13 60, DEFAULT_60HZ_VBLANK_DURATION, /* frames per second, vblank duration */ 14 1, /* single CPU, no need for interleaving */ 15 0, 16 17 /* video hardware */ 18 32*8, 32*8, { 0*8, 32*8-1, 0*8, 28*8-1 }, 19 satansat_gfxdecodeinfo, 20 32,4*4 + 4*4, 21 satansat_vh_convert_color_prom, 22 23 VIDEO_TYPE_RASTER | VIDEO_SUPPORTS_DIRTY, 24 0, 25 generic_vh_start, 26 generic_vh_stop, 27 satansat_vh_screenrefresh, 28 29 /* sound hardware */ 30 0,0,0,0 31 }; Line 1 begins the definition of the MachineDriver for this game. Lines 4-12 define the cpu that this game uses. It is possible for a game to have multiple CPUs. Each cpu would have to have it's own MachineCPU structure and each would be listed one after the other in the MachineDriver structure. Zarzon only uses one cpu. Line 6 defines the type of CPU, in this case a 6502. Line 7 is the clock speed that the CPU is running at. This is important to keeping the game running at the correct speed. For Zarzon it is 11289000/16. It is written this way because the main clock on the Zarzon board is 11289000 and is divided down by 16 by hardware on the board. Line 8 is the memory region that this processor uses. When you load ROMS you will define one or more memory regions to hold the data from the ROMS. Each processor will have it's own memory region and there may be other memory regions allocated for things like graphics ROMS. In this case this cpu uses memory region 0. Line 9 contains 4 pointers to the structures that define how to handle memory and IO accesses. The pointers are Memory Read, Memory Write, IO Read, IO Write. The 6502 does not have IO access so these are not defined. Line 10 determines how vertical blank interrupts are handled. The first value is a pointer to the routine that will handle the interrupt. The second value is how many interrupts there should be per frame, this is usually 1. Line 13 indicates how many video frames per second the game hardware generates and the second value on that line indicates how long the VBLANK should last (see driver.h for more info on this). Line 14 is the CPU interleave value. This determines how often MAME switches back and forth between each of the CPU's in the machine. The higher the value the more often MAME will switch between processor. The more often it switches, the slower the emulation will be, but it is sometimes necessary to keep processors in sync with each other. Since Zarzon uses only one CPU this value is set to 1. Line 15 is a pointer to a routine that is used to do any special initialization that this machines hardware needs. Line 18 defines the height and width of the games screen in pixels, and how big the visible area of the screen is. Since Zarzon uses tile based graphics the 32*8 notation is used to indicate that the screen is 32 tiles across and each tile is 8 pixels wide, the screen is also 32*8 pixels high. The next four values define the corners of the visible area of the screen. For zarzon the upper left corner is at 0,0 and the lower right is at 255,223. Line 19 is a pointer to the data structure that defines how the graphics in the graphics ROMS are formatted. In line 20 the first value is the number of colors in the games color palette. The second value is the number of entries in the game's color lookup table. Line 21 is a pointer to a routine that is called to convert the raw color prom data into actual palette RGB values. This routine is usually found in /vidhrdw/. Line 23 is the video attributes. The attributes for this game indicate that it is a raster game (as opposed to vector) and that it supports dirty rectangle handling. For a complete list of attributes look at driver.h. Line 24 ??? Lines 25,26 and 27 are pointers to the routines to start the display hardware, stop the display hardware and refresh the screen respectively. Zarzon uses MAME's generic start and stop routines since it does not need any special setup for video. Most games will have their own screen refresh routine which is the routine that actually draws the video display for the game. The remainder of this structure describes the sound hardware. Since Zarzon does not currently support sound I won't discuss this part of the driver at this point. 2.3 ROM Definition The next section of the driver defines what ROM images the game needs to load and where to load them. This section does not look like normal C code since macros are used to make it more readable. 1 ROM_START( zarzon_rom ) 2 ROM_REGION(0x10000) /* 64k for code */ 3 ROM_LOAD( "ZARZ122.07", 0x4000, 0x0800, 0xa260b9f8 ) 4 ROM_LOAD( "ZARZ123.08", 0x4800, 0x0800, 0xf2b8072c ) 5 ROM_LOAD( "ZARZ124.09", 0x5000, 0x0800, 0xdea47b9a ) 6 ROM_LOAD( "ZARZ125.10", 0x5800, 0x0800, 0xa30532d5 ) 7 ROM_LOAD( "ZARZ126.13", 0x6000, 0x0800, 0x043c84ba ) 8 ROM_LOAD( "ZARZ127.14", 0x6800, 0x0800, 0xa3f1286b ) 9 ROM_LOAD( "ZARZ128.15", 0x7000, 0x0800, 0xfbc89252 ) 10 ROM_LOAD( "ZARZ129.16", 0x7800, 0x0800, 0xc7440c84 ) 11 ROM_RELOAD( 0xf800, 0x0800 ) /* for the reset/interrupt vectors */ 12 ROM_LOAD( "ZARZ130.22", 0x8000, 0x0800, 0x78362c82 ) 13 ROM_LOAD( "ZARZ131.23", 0x8800, 0x0800, 0x566914b5 ) 14 ROM_LOAD( "ZARZ132.24", 0x9000, 0x0800, 0x7c4f3143 ) 15 16 ROM_REGION(0x1000) /* temporary space for graphics (disposed after conversion) */ 17 ROM_LOAD( "ZARZ135.73", 0x0000, 0x0800, 0xbc67fa61 ) 18 ROM_LOAD( "ZARZ136.75", 0x0800, 0x0800, 0x2364fe46 ) 19 20 ROM_REGION(0x1000) /* sound data for Vanguard-style audio section */ 21 ROM_LOAD( "ZARZ133.53", 0x0000, 0x0800, 0x4b404b14 ) 22 ROM_LOAD( "ZARZ134.54", 0x0800, 0x0800, 0x01380400 ) 23 ROM_END Line 1 defines that start of the ROM info for this game the name in parenthesis (zarzon_rom) is the name that goes into the GameDriver structure. Line 2 defines that start of a ROM region. The value in parenthesis is the size of the region. This line for example define ROM region 0 to be 64K in size. Lines 3 through 14 are the ROMs that are loaded into the current ROM region. The first parameter in each line is the name of the ROM file to load. The second parameter tells where in that memory region the ROM should be loaded. The third parameter tells how many bytes long the ROM is. The final parameter is the checksum of the ROM that is used to validate if the ROM is correct or not. For example, line 3 would load 0x800 bytes from the file ZARZ122.07 starting at address 0x4000 in memory region 0. Line 11 is a little different then the others. This line is a ROM_RELOAD instead of a ROM_LOAD. ROM_RELOAD is used to reload the ROM that was just loaded into a different area of memory. So line 10 loads ZARZ129.16 into memory at 0x7800, and line 11 loads it again at memory location 0xf800. The main purpose of this is that if the rom is missing or has a bad checksum it will not be reported twice and an error like it would if you simply used ROM_LOAD again. Line 16 ends ROM region 0 and starts ROM region 1. In Zarzon this region of memory is used to store the graphics ROMs. Line 20 ends ROM region 1 and starts ROM region 2 which is used to store the PROMS that are used for sound generation. Zarzon does not support sound yet so these ROMs just go unused. Line 23 ends the definition of the ROMs. 2.4 MemoryReadAddress Structure The MemoryReadAddress structure defines the memory map for the machine. It tells MAME what it should do when the processor wants to read data from a specific memory location. Here is the MemoryReadAddress structure for Zarzon (remember Zarzon is a clone of Satan of Saturn, thus the name satansat_readmem): 1 static struct MemoryReadAddress satansat_readmem[] = 2 { 3 { 0x0000, 0x1fff, MRA_RAM }, 4 { 0x4000, 0x97ff, MRA_ROM }, 5 { 0xb004, 0xb004, input_port_0_r }, /* IN0 */ 6 { 0xb005, 0xb005, input_port_1_r }, /* IN1 */ 7 { 0xb006, 0xb006, input_port_2_r }, /* DSW */ 8 { 0xb007, 0xb007, input_port_3_r }, /* IN2 */ 9 { 0xf800, 0xffff, MRA_ROM }, 10 { -1 } /* end of table */ 11 }; Line 1 start the definition of the structure. The name, satansat_readmem in this case, goes into the MachineDriver structure. Each line in the structure starts with 2 numbers that are the starting and ending address of a section of memory. Following this are one or more items that define what this section of memory is and how it should be handled. Line 3 defines a memory region starting at 0x0000 and ending at 0x1FFF. It is defined as MRA_RAM which means that the region is RAM. When a read is done to an address in this section it will come from the corresponding location in the memory region (defined when you loaded the ROMS) for this processor. Line 4 is defined as MRA_ROM which means this section is ROM. This essentially behaves the same way as MRA_RAM. Line 5 shows a slightly different type of memory section. Since the start and end addresses are the same it means that this section is only one byte long, which is perfectly legal to do. For the function of this region instead of having a pre-defined function it has the name of a routine. When the processor reads from this memory location MAME will call the routine input_port_0_r and this routine will return the appropriate value. In this situation input_port_0_r is a function defined within MAME but it could have just as easily been a routine you wrote yourself. input_port_0_r will be discussed in further detail in the section on the IO structures. Line 6,7, and 8 all function basically the same as line 5. Line 9 defines a final piece of ROM from address 0xf800 to 0xffff. Line 10 is set to -1 to indicate the end of the table 2.5 MemoryWriteAddress Structure The MemoryWriteAddress structure is setup basically the same as MemoryReadAddress, but instead of handling reads from memory it handles writes to memory. 1 static struct MemoryWriteAddress satansat_writemem[] = 2 { 3 { 0x0000, 0x03ff, MWA_RAM }, 4 { 0x0400, 0x07ff, MWA_RAM, &rockola_videoram2 }, 5 { 0x0800, 0x0bff, videoram_w, &videoram, &videoram_size }, 6 { 0x0c00, 0x0fff, colorram_w, &colorram }, 7 { 0x1000, 0x1fff, rockola_characterram_w, &rockola_characterram }, 8 { 0x4000, 0x97ff, MWA_ROM }, 9 // { 0xb000, 0xb000, satansat_sound0_w }, 10 // { 0xb001, 0xb001, satansat_sound1_w }, 11 { 0xb002, 0xb002, satansat_b002_w }, /* flip screen & irq enable */ 12 { 0xb003, 0xb003, satansat_backcolor_w }, 13 { -1 } /* end of table */ 14 }; Line 3 defines a section of memory from location 0x0000 to 0x03FF and defines it as being RAM. Line 4 looks similar to line 3 in that it also defines the section as RAM, but it has one more parameter then line 3. The &rockola_videoram2 is a pointer variable that is declared as: unsigned char *rockola_videoram2; When MAME sets up the memory for this machine it will put the address of the memory section 0x400-0x7ff in the pointer rockola_videoram2. This will allow this section of memory to be accessed easily in other parts of the driver. It is important to remember that the memory for this section is still in the memory region declared during ROM loading, rockola_videoram2 is simply a pointer to a part of this region. Line 5 gets a little more complicated. As usual we define the memory section, 0x800 to 0xbff in this case. Next we have videoram_w, which is a routine that will be called when ever a write is done to this section. videoram_w happens to be a predefined routine in MAME. Next we have &videoram which is a pointer variable just like in line 4. When the memory is setup &videoram will point to this section of memory. Finally we have &videoram_size. When memory is setup MAME will put the size of this memory region into the variable videoram_size. Line 6 works just like line 5 but in this case we do not need the size of this section on memory so the size variable is not included. colorram_w and colorram are both defined in the MAME core. Line 7 is just like line 6 except that custom variables are used instead of using standard built in ones. Line 8 defines a section of memory as ROM. Lines 9-12 are similar to lines 5-8 in MemoryReadAddress. When these addresses are written to the corresponding routines are called to handle the write. Line 13 once again we end the structure with a -1. 2.6 Input Ports The next section of the driver we will look at defines the operation the machines input ports. This structure is used to define things like joystick inputs, coin inputs, dipswitches, etc. I have broken this section up into a couple smaller sections: 1 INPUT_PORTS_START( satansat_input_ports ) 2 PORT_START /* IN0 */ 3 PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT | IPF_2WAY ) 4 PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT | IPF_2WAY ) 5 PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) 6 PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT | IPF_2WAY | IPF_COCKTAIL ) 7 PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT | IPF_2WAY | IPF_COCKTAIL ) 8 PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 | IPF_COCKTAIL ) 9 PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) 10 PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 | IPF_COCKTAIL ) Line 1 begins the definition of the input ports for this game. The name, satansat_input_port goes into the GameDriver structure. Line 2 begins the definition of the first port (input port 0). Lines 3 through 10 define what each bit in this port does. The first parameter in each line is the mask. Each bit that is a 1 in the mask will be effected by the function of this line. So in line 3 the mask is 0x01 which means that this line effects bit 0. The next parameter sets whether this function is active high or active low. the final parameter sets the function for that bit and any special attributes it has. In line 3 the function is IPT_JOYSTICK_LEFT which indicates that this bit is controlled by the joystick being pushed left. The IP_ACTIVE_HIGH parameter in this line would mean that when the stick is pushed left this bit should be a 1 and at all other times it should be a 0. The IPF_2WAY attribute indicates that this is a 2 way joystick, it can only be pushed left and right. Line 4 is the same as line 3 but for JOYSTICK_RIGHT. Line 5 defines a button. BUTTON1 is usually the 'fire' button for the game. Line 6,7,8 define a second 2 way joystick and button, but this one has the attribute IPF_COCKTAIL. This indicates that this stick is used in the cocktail table version of the machine. Line 9 and 10 define a second button for both the normal machine and the cocktail version. This is the next section of the input ports, it defines the bits for port 1: 1 PORT_START /* IN1 */ 2 PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) 3 PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) 4 PORT_BIT( 0x7C, IP_ACTIVE_HIGH, IPT_UNKNOWN ) 5 PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) Line 1 starts the second port (port1). Lines 2 and 3 are similar to the lines in the first input port. Line 2 defines START1 which is usually the 1 player start button and line 2 defines START2 which is usually the 2 player start button. If you look at line 4 you will notice that there is more then 1 bit set in the mask. You can have as many bits in the mask set to 1 as you like in each entry. Each bit that is set to 1 will be effected by the function of the line. The function for line 4 is IPT_UNKNOWN which means that the function of that bit is not currently known. Since this line is defined as IP_ACTIVE_HIGH, and unknown inputs can never become active, these bits will always be 0. Line 5 is similar to line 4, but since it is IP_ACTIVE_LOW the bit will always be high. The next section is a little different since it defines a dipswitch instead of inputs. Even though it is a dipswitch it is still referred to as an input port, in this case input port 2. 1 PORT_START /* DSW */ 2 PORT_DIPNAME( 0x01, 0x01, "Cabinet", IP_KEY_NONE ) 3 PORT_DIPSETTING( 0x01, "Upright") 4 PORT_DIPSETTING( 0x00, "Cocktail" ) 5 PORT_DIPNAME (0x0a, 0x00, "Coinage", IP_KEY_NONE ) 6 PORT_DIPSETTING ( 0x08, "2 Coins/1 Credit" ) 7 PORT_DIPSETTING ( 0x00, "1 Coin/1 Credit" ) 8 PORT_DIPSETTING ( 0x02, "1 Coin/2 Credits" ) 9 /* 0x0a gives 2/1 again */ 10 PORT_DIPNAME (0x04, 0x00, "Bonus Life", IP_KEY_NONE ) 11 PORT_DIPSETTING ( 0x00, "5000" ) 12 PORT_DIPSETTING ( 0x04, "10000" ) 13 PORT_DIPNAME (0x30, 0x00, "Lives", IP_KEY_NONE ) 14 PORT_DIPSETTING ( 0x00, "3" ) 15 PORT_DIPSETTING ( 0x10, "4" ) 16 PORT_DIPSETTING ( 0x20, "5" ) 17 /* 0x30 gives 3 again */ 18 PORT_DIPNAME (0x40, 0x00, "Unknown", IP_KEY_NONE ) 19 PORT_DIPSETTING ( 0x00, "Off" ) 20 PORT_DIPSETTING ( 0x40, "On" ) 21 PORT_DIPNAME (0x80, 0x00, "Unknown", IP_KEY_NONE ) 22 PORT_DIPSETTING ( 0x00, "Off" ) 23 PORT_DIPSETTING ( 0x80, "On" ) Line 1 defines the start of the input port as usual. Line 2 defines that start of the first section of the dipswitch. The first parameter is the mask just like in a normal input port. Each bit that is a 1 in the mask will be effected by this dipswitch. The next parameter is the default value for the dipswitch, in this case it is 0x01. The next parameter is the name of the dipswitch, this will be displayed in the dipswitch menu in MAME. The final parameter allows you to assign a key to control this dipswitch. Since it is IP_KEY_NONE is means that there is no key to control it and it has to be set on the dipswitch menu. Lines 3 and 4 are PORT_DIPSETTINGs. These define the different settings for the dipswitch section that we just defined. The first parameter in PORT_DIPSETTING is the value that corresponds to the setting, and the second parameter is the name of that setting. For example if the dipswitch "Cabinet" is set to "Upright" then bit 0 of the port will be set to 0x01. If it is set to "Cocktail" then bit 0 of the port will be set to 0x00. Line 5 begins the definition of the next section of the dipswitch, this time called "Coinage". Notice that there are two bits set in the mask meaning that this dipswitch section can have more then two combination. Lines 6-8 define the settings for the "Coinage" dipswitch. There are four possible combinations for this dip section, but only 3 of them are defined. As indicated by the comment in line 9, the fourth setting, 0x0A, is the same as the setting for 0x08. Lines 13-23 define the remained of the dipswitch sections and settings. Here is the final section of the input ports: 1 PORT_START /* IN2 */ 2 PORT_BITX(0x01, IP_ACTIVE_HIGH, IPT_COIN1 | IPF_IMPULSE, 3 IP_NAME_DEFAULT, IP_KEY_DEFAULT, IP_JOY_DEFAULT, 1 ) 4 PORT_BIT( 0x0e, IP_ACTIVE_LOW, IPT_UNUSED ) 5 PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN ) /* connected to a counter - random number generator? */ 6 INPUT_PORTS_END Line 1 starts the definition of the final input port, port 3. Line 2 is another type of port bit definition. PORT_BITX allows us to specify some extra parameters for the port. The function of the bit is IPT_COIN1 which is the first coin input. The attribute IPF_IMPULSE indicates that this is an impulse input meaning the bit goes active for a period of time and then goes back inactive on it's own. The only additional parameter that is used in this line is the 1 at the end which is the length of the impulse time. All the other additional parameters are set to DEFUALT since we do not need them. Line 4 shows one more different function, IPT_UNUSED. This indicates that these bits are not used by the game at all, usually meaning that they are not even connected in the hardware. Just like UNKNOWN these can never become active. Since the bit is defined as IP_ACTIVE_LOW the bits will always be high. Line 6 marks the end of the input ports for this machine. 2.7 GfxDecodeInfo Structure GfxDecodeInfo is the first of two structures that determine how the graphics data for the game will be decoded. Graphics data is usually stored in ROMS on the game board and these ROMS are organized in such a way as to make it easy for the circuitry to draw the display image. Unfortunately the way the data is stored in ROMS is usually not convenient for use in emulation. The GfxDecode structures tell MAME how to convert the raw graphics ROMS into a more useable format. 1 static struct GfxDecodeInfo satansat_gfxdecodeinfo[] = 2 { 3 { 0, 0x1000, &charlayout256, 0, 4 }, /* the game dynamically modifies this */ 4 { 1, 0x0000, &charlayout256, 4*4, 4 }, 5 { -1 } 6 }; Line 1 begins the definition of the GfxDecodeInfo structure. The name, satansat_gfxdecodeinfo in this case, goes into the MachineDriver structure. This machine has two different methods that are needed to decode the graphics, lines 3 and 4 each define one of these methods. The first parameter in each of these lines tells MAME in which memory region the data for the graphics can be found. Line 4 is pretty obvious. It indicates that the graphics data can be found in region 1 which if you look at the ROM load structure you can see that the graphics ROMS are loaded into this region. Line 3 indicates that the data comes from region 0 which is the processor's memory region. The reason for this is that Zarzon stores some of it's graphics data in RAM. The next parameter tells where within that region the data comes from. The third parameter is a pointer to a GfxLayout structure which defines exactly how the graphics data is formated. The final 2 parameters in each line are used to determine the colors to draw the graphics with. The first number in this pair is the offset into the color lookup table where the colors for these graphics starts. The second number in the pair is the total number of colors this graphic uses. 2.8 GfxLayout structure The GfxLayout structure is the second part of defining how the graphics ROMS are formatted. This structure defines the actual format of the data. 1 static struct GfxLayout charlayout256 = 2 { 3 8,8, /* 8*8 characters */ 4 256, /* 256 characters */ 5 2, /* 2 bits per pixel */ 6 { 0, 256*8*8 }, /* the two bitplanes are separated */ 7 { 0, 1, 2, 3, 4, 5, 6, 7 }, 8 { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, 9 8*8 /* every char takes 8 consecutive bytes */ 10 }; This can be a little confusing so follow it carefully. Line 1 begins the structure. The name, charlayout256 in this case, goes into the GfxDecodeInfo structure. Line 3 defines the height and width of each character in pixels. So the characters in Zarzon are 8 pixels wide by 8 pixels high. Line 4 defines how many characters are in the graphics ROMS, in this case there are 256 characters. Line 5 defines how many bits are needed for each pixel in the character. Zarzon has 2 bits per pixel. Since there are 4 different combinations of 2 bits (00,01,10,11) each character can have up to 4 colors. Line 6 contains the offset in bits to the beginning of the data for each bit plane. Since Zarzon has 2 bits per pixel (as defined in line 5) it also has 2 bit planes. In the Zarzon hardware each of the two bits that makes up each pixel is stored in a separate ROM chip. The first graphics ROM we loaded contains the first bit plane, so the first bit plane offset is 0. The second ROM we loaded contains the second bit plane so to get to it we have to skip over all the data in the first ROM. The offset to the second plane is 256*8*8, where does this number come from? The 256 is for the 256 characters in the ROM, the first *8 is the because each character is 8 lines high so there are 8 bytes for each character. 256*8=2048 which is the length of the graphics ROM in bytes. The bit plane offsets are in units of bits, since there are 8 bits in each line of each character we have to multiply the 2048 x 8 giving us a total offset of 256*8*8 = 16384. Line 7 and 8 tell where to get the data for each pixel at each coordinate in the characters. The characters in Zarzon are 8x8 pixels. Line 7 gives the offset for each pixel across the character. Line 8 gives the offset for each line down the character. This sounds a little confusing but it will be clearer when we look at an example. Line 9 gives the distance in bits between two consecutive characters. Each character in Zarzon requires 8 bytes and each byte has 8 bits, so the total is 8*8 = 64. Ok, now lets look at how this all works together. When MAME decodes the graphics ROMS it steps through the characters one bye one. So lets say it wants to decode character number 10. Zarzon uses 2 bit planes (line 5) so MAME first has to find the start of the data for each bit plane. From the GfxDecodeInfo structure we know that the graphics ROMS are in memory region 1 starting at offset 0. From line 6 we know that bit plane one starts at offset 0 and the bit plane 2 starts at offset 16384. From line 9 we know that each character requires 64 bits, so we can calculate where the data for each bit plane starts for this character: Bit plane 1, 10th character * 64 bits per character + 0 plane offset = 640 Bit plane 2, 10th character * 64 bits per character + 16384 plane offset = 17024 Now that we know where in the data each bit plane starts, we can start decoding the character. We start with the pixel in the upper left corner of the character. This will be the first pixel in the first line. To find the offset to this bit in the data look at the first value in line 8, 0*8, this is the offset to the data for the first line on the character. Next look at the first value in line 7, 0, this is the offset to the data for the first pixel in the each line. So the offset to the data for this pixel will be (0*8) + 0. For the second pixel on the first line we have 0*8 again since we are still on the first line but now we use the second value in line 7, which is 1. The offset for the second pixel will be (0*8)+1. We continue the same way for each of the 8 pixel on the first line. To get the first pixel in the second line of the character we start with the second value in line 8 which is 1*8. The first pixel in the second line will be (1*8)+0 = 8, the second pixel will be (1*8)+1 = 9, etc. 3.0 Linking In The Driver. The final thing I will cover in this volume is how to link your driver into the MAME source. First of all you have to put the names of all your source files in the MAKEFILE. Since Zarzon is in the rockola driver source it will look like this in the MAKEFILE: obj/vidhrdw/rockola.o obj/sndhrdw/rockola.o obj/drivers/rockola.o \ This will compile the rockola driver and link it into MAME, but MAME still does not know that the game exists, this requires one more step. In the file driver.c there is an array of pointers to each of the GameDriver structures in MAME. The line for zarzon looks like this: &zarzon_driver, /* (c) 1981 Taito, gameplay says SNK */ You also need to declare this in driver.c so that the compiler can find it: extern struct GameDriver zarzon_driver; That's all there is to linking the driver into the code.